Add shared base for KIP-1356 IQv2 headers-aware integration tests - #4461
Add shared base for KIP-1356 IQv2 headers-aware integration tests#4461Jess668 wants to merge 2 commits into
Conversation
Introduces HeadersIQv2IntegrationTestBase: shared cluster/serde/streams-lifecycle and output-topic barrier helpers, plus sendAndCapture (captures the produced schema-id GUID) and assertSchemaIdHeaders (17-byte MAGIC_BYTE_V1 format + byte equality vs the produced GUID) for the KIP-1356 headers-aware IQv2 tests.
|
🎉 All Contributor License Agreements have been signed. Ready to merge. |
Alieh Saeedi (aliehsaeedii)
left a comment
There was a problem hiding this comment.
Thanks Jess668. Made the 1st pass.
| public abstract class HeadersIQv2IntegrationTestBase extends ClusterTestHarness { | ||
|
|
||
| /** GUID bytes the producer wrote for the key / value schema, captured on send (see below). */ | ||
| private byte[] expectedKeyGuid; |
There was a problem hiding this comment.
These hold only the last captured GUID, so assertSchemaIdHeaders checks every record against the most recent send. That is correct only because every record in these tests shares one key and one value schema — a subclass with two schemas would compare against the wrong GUID. Returning the captured bytes from sendAndCapture and passing them in would make it per-record, as the javadoc claims.
| */ | ||
| protected void assertSchemaIdHeaders(Headers headers, String context) { | ||
| byte[] keyBytes = assertGuidHeader(headers, SchemaId.KEY_SCHEMA_ID_HEADER, "Key", context); | ||
| if (expectedKeyGuid != null) { |
There was a problem hiding this comment.
If a subclass asserts headers on a record it did not produce through sendAndCapture, both byte-equality checks are skipped and this passes as a format-only check with no signal. Add assertNotNull(expectedKeyGuid, ...) so a missed capture fails.
| boolean running = false; | ||
| try { | ||
| running = startedLatch.await(timeoutSeconds, TimeUnit.SECONDS); | ||
| assertTrue(running, "KafkaStreams should reach RUNNING state"); |
There was a problem hiding this comment.
A startup failure never counts the latch down, so this waits out the full timeout (90s in the restore tests) and reports only "should reach RUNNING state", leaving the real exception in the log. TimestampedKeyValueStoreWithHeadersIntegrationTest keeps the last observed state in an AtomicReference and puts it in the message — good to carrying that over, plus counting down on terminal states so it fails fast.
| int timeoutSeconds, Integer commitIntervalMs) throws Exception { | ||
| CountDownLatch startedLatch = new CountDownLatch(1); | ||
| KafkaStreams streams = new KafkaStreams(topology, createStreamsProps(appId, commitIntervalMs)); | ||
| streams.cleanUp(); |
There was a problem hiding this comment.
cleanUp() and start() are outside the try, so if either throws, streams is never closed and the caller never gets the reference to close it either — the state-directory lock and any started StreamThreads leak for the rest of the fork JVM. Move both inside the try.
|
|
||
| protected void closeStreams(KafkaStreams streams) { | ||
| if (streams != null) { | ||
| streams.close(Duration.ofSeconds(10)); |
There was a problem hiding this comment.
close(Duration) returns false on timeout instead of throwing, and that result is dropped. The restore tests close and immediately restart on the same application.id and state dir, so a timed-out shutdown shows up later as a confusing cleanUp() failure — assert the return value here.
| } | ||
| } | ||
| } | ||
| assertEquals(expectedCount, results.size(), |
There was a problem hiding this comment.
The loop stops at >= expectedCount but this asserts exact equality, so a single at-least-once duplicate fails the barrier instead of passing it. Every caller discards the returned list, so assertTrue(results.size() >= expectedCount, ...) is enough.
| protected void createTopicsWithPartitions(int numPartitions, String... topicNames) | ||
| throws Exception { | ||
| Properties adminProps = new Properties(); | ||
| adminProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList); |
There was a problem hiding this comment.
This is an AdminClient, so AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG is the right constant — same value, and it's what the rest of the repo uses.
| try (KafkaProducer<GenericRecord, GenericRecord> producer = | ||
| new KafkaProducer<>(createProducerProps())) { | ||
| sendAndCapture(producer, new ProducerRecord<>(topic, null, timestamp, key, value)); | ||
| producer.flush(); |
There was a problem hiding this comment.
sendAndCapture already blocks on send().get(), so this flush (and the one in produceAll) is a no-op — drop it.
| } | ||
| } | ||
|
|
||
| protected void produce(String topic, GenericRecord key, GenericRecord value, long timestamp) |
There was a problem hiding this comment.
The KV-store test in #4462 reimplements this instead of calling it — its produceTombstone is just produce(topic, key, null, ts). Can it reuse these two?
| // Header-mode serdes and client configs | ||
| // --------------------------------------------------------------------------------------------- | ||
|
|
||
| protected GenericAvroSerde createKeySerde() { |
There was a problem hiding this comment.
Each serde owns a CachedSchemaRegistryClient and its HTTP connection pool, and nothing closes them — Streams does not close serdes passed via Consumed.with/Produced.with/StoreBuilder. Every other client here is in a try-with-resources; these leak one pool each per topology build.
- Use AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG for the admin client - Fail fast in startStreamsAndAwaitRunning on terminal states and report the last observed state; move cleanUp()/start() inside the try - Assert KafkaStreams.close(Duration) did not time out - Accept at-least-once duplicates in the output-topic barrier - Drop redundant producer.flush() after blocking send().get() - Capture the produced schema-id GUIDs per record; a null/missing capture now fails rather than silently degrading to a format-only check - Close created serdes in teardown
What
Introduces HeadersIQv2IntegrationTestBase: shared cluster/serde/streams-lifecycle and output-topic barrier helpers, plus sendAndCapture (captures the produced schema-id GUID) and assertSchemaIdHeaders (17-byte MAGIC_BYTE_V1 format + byte equality vs the produced GUID) for the KIP-1356 headers-aware IQv2 tests.
Checklist
Please answer the questions with Y, N or N/A if not applicable.
References
JIRA:
Test & Review
Open questions / Follow-ups